package com.intramirror.wd.business.config;

import com.intramirror.common.jdbc.interceptor.ExecutorInterceptor;
import com.intramirror.common.jdbc.interceptor.StatementInterceptor;
import com.intramirror.common.jdbc.persistence.datasource.ReadWriteDataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import org.springframework.transaction.interceptor.*;

import javax.sql.DataSource;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Created by zhongyu on 2017/12/15.
 */
@Configuration
@EnableTransactionManagement
@AutoConfigureAfter({ DataSourceConfiguration.class })
@MapperScan("com.intramirror.wd.business.mapper")
public class MyBatisConfig implements TransactionManagementConfigurer {

	@Autowired
	private DataSourceConfiguration dataSourceConfiguration;

	@Autowired
	private PlatformTransactionManager transactionManager;

	private ReadWriteDataSource readWriteDataSource = new ReadWriteDataSource();

	private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.intramirror.wd.business.manage.*.*(..))";

	@Bean(name = "sqlSessionFactory")
	public SqlSessionFactory sqlSessionFactoryBean() {
		initDatasource();
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(readWriteDataSource);

		//添加插件
		bean.setPlugins(new Interceptor[]{new ExecutorInterceptor(), new StatementInterceptor()});

		//添加XML目录
		ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
		try {
			bean.setMapperLocations(resolver.getResources("classpath:mapper/**/**.xml"));
			return bean.getObject();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	@Bean
	public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
		return new SqlSessionTemplate(sqlSessionFactory);
	}

	@Bean
	@Override
	public PlatformTransactionManager annotationDrivenTransactionManager() {
		return new DataSourceTransactionManager(readWriteDataSource);
	}

	private void initDatasource(){
		readWriteDataSource.setWriteDataSource(dataSourceConfiguration.writeDataSource());

		Map<String, DataSource> readDataSourceMap = new HashMap<>();
		readDataSourceMap.put("readDataSource1", dataSourceConfiguration.readDataSourceOne());
		readDataSourceMap.put("readDataSource2", dataSourceConfiguration.readDataSourceTwo());
		readWriteDataSource.setReadDataSourceMap(readDataSourceMap);

		readWriteDataSource.init();
	}

	@Bean
	public TransactionInterceptor txAdvice() {
		NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();

		/*只读事务，不做更新操作*/
		RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
		readOnlyTx.setReadOnly(true);
		readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);

		/*当前存在事务就使用当前事务，当前不存在事务就创建一个新的事务*/
		RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
		requiredTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Throwable.class)));
		requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
		//requiredTx.setTimeout(TX_METHOD_TIMEOUT);
		Map<String, TransactionAttribute> txMap = new HashMap<>();
		txMap.put("*WithTx", requiredTx);
		txMap.put("*", readOnlyTx);
		source.setNameMap(txMap);
		TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);
		return txAdvice;
	}

	@Bean
	public Advisor txAdviceAdvisor() {
		AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
		pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
		return new DefaultPointcutAdvisor(pointcut, txAdvice());
	}
}
